#include "drawTool.h"
#include "lyn_plugin_dev.h"

#include "freetype/freetype.h"

#include <map>
#include <mutex>

#define CHECK_RETURN(cond, ret, log)                                           \
  if (cond) { /*LOG(ERROR) << log;*/                                           \
    return ret;                                                                \
  }

struct YUVColor {
  uint8_t colorY;
  uint8_t colorU;
  uint8_t colorV;
};

std::map<DrawColor, YUVColor> g_sampleColorMap = {
    {SAMPLE_DRAW_COLOR_WHITE, {180, 128, 128}},
    {SAMPLE_DRAW_COLOR_BLACK, {16, 128, 128}},
    {SAMPLE_DRAW_COLOR_RED, {65, 100, 212}},
    {SAMPLE_DRAW_COLOR_GREEN, {112, 72, 58}},
    {SAMPLE_DRAW_COLOR_BLUE, {35, 212, 114}},
    {SAMPLE_DRAW_COLOR_YELLOW, {162, 44, 142}},
    {SAMPLE_DARW_COLOR_CYAN, {131, 156, 44}}};

struct TextBitMap {
  uint8_t *buffer = nullptr;
  int w = 0;
  int h = 0;
  // char ch;
  wchar_t ch;
  FontSize fontSize;
};

namespace sampleFreeType {
static bool s_init = false;
static int s_fontSize = 0;
static std::mutex s_drawTextMux;
static FT_Library ftLib;
static FT_Face ftFace;
static std::map<uint32_t, TextBitMap> s_textMap;

int Init() {
  std::lock_guard<std::mutex> lock(s_drawTextMux);
  int ret = 0;
  if (s_init == false) {
    ret = FT_Init_FreeType(&ftLib);
    CHECK_RETURN(ret, lynEDRAWTEXT, "error ret " << ret);

    ret = FT_New_Face(ftLib, "/usr/share/Monaco_Linux.ttf", 0, &ftFace);
    CHECK_RETURN(ret, lynEDRAWTEXT, "error ret " << ret);

    ret = FT_Select_Charmap(ftFace, FT_ENCODING_UNICODE);
    CHECK_RETURN(ret, lynEDRAWTEXT, "error ret " << ret);
    s_init = true;
  }
  return ret;
}

TextBitMap *SelectSingleWchar(wchar_t ch, FontSize fontSize) {
  uint32_t key = ch;
  TextBitMap bitMap;
  key = (key << 8) + fontSize;
  std::lock_guard<std::mutex> lock(s_drawTextMux);
  if (s_fontSize != fontSize) {
    FT_Set_Char_Size(ftFace, 0, fontSize * 64, 0, 96);
    FT_Set_Pixel_Sizes(ftFace, 0, fontSize);
    s_fontSize = fontSize;
  }
  auto it = s_textMap.find(key);
  if (it == s_textMap.end()) {
    auto index = FT_Get_Char_Index(ftFace, ch);
    FT_Load_Glyph(ftFace, index, FT_LOAD_DEFAULT);
    FT_Render_Glyph(ftFace->glyph, FT_RENDER_MODE_NORMAL);
    bitMap.w = ftFace->glyph->bitmap.width;
    bitMap.h = ftFace->glyph->bitmap.rows;
    bitMap.buffer = (uint8_t *)malloc(bitMap.w * bitMap.h);
    bitMap.ch = ch;
    bitMap.fontSize = fontSize;
    // size += bitMap.w * bitMap.h;
    // printf("malloc total %d size %d font %d ch %c\n", size, bitMap.w *
    // bitMap.h, bitMap.fontSize, bitMap.ch);
    memcpy(bitMap.buffer, ftFace->glyph->bitmap.buffer, bitMap.w * bitMap.h);
    s_textMap[key] = bitMap;
  }
  return &s_textMap[key];
}

} // namespace sampleFreeType

inline void yuvSetdata(uint8_t *YBuff, uint8_t *UVBuff,
                       lynPixelFormat_t yuvType, uint32_t width,
                       uint32_t height, DarwPoint &drawPoint, uint8_t YColor,
                       uint8_t UColor, uint8_t VColor, bool updateUV) {
  *(YBuff + drawPoint.y * width + drawPoint.x) = YColor;

  // update U V
  uint32_t u_offset = 0, v_offset = 0;
  // uint32_t plane_size = width * height / 4;
  switch (yuvType) {
  case LYN_PIX_FMT_NV12: {
    u_offset = (drawPoint.y / 2) * width + drawPoint.x / 2 * 2;
    *(UVBuff + u_offset) = UColor;
    *(UVBuff + u_offset + 1) = VColor;
    return;
  } break;

  case LYN_PIX_FMT_NV21: {
    v_offset = (drawPoint.y / 2) * width + drawPoint.x / 2 * 2;
    u_offset = v_offset + 1;
  } break;

  case LYN_PIX_FMT_YUV420P: {
    u_offset = drawPoint.y / 2 * width / 2 + drawPoint.x / 2;
    v_offset = u_offset + width * height / 4;
  } break;
  default: { return; }
  }
  UVBuff[u_offset] = UColor;
  UVBuff[v_offset] = VColor;
}

int PluginDrawTextV2(DrawTextAttrV2 *attr) {
  if (attr == nullptr || attr->imgData == nullptr || attr->text == nullptr) {
    return lynEPTR;
  }

  if (attr->imgFmt != LYN_PIX_FMT_YUV420P && attr->imgFmt != LYN_PIX_FMT_NV12 &&
      attr->imgFmt != LYN_PIX_FMT_NV21) {
    // LOG_PLUGIN_W("format %d not support\n", attr->imgFmt);
    return lynEFORMAT;
  }
  int ret = 0;
  ret = sampleFreeType::Init();
  if (ret != 0) {
    return ret;
  }

  unsigned char *data_y = attr->imgData;
  unsigned char *data_u = attr->imgData + attr->imgW * attr->imgH;
  // unsigned char * data_v = attr->imgData + (attr->imgW * attr->imgH) * 5 / 4;

  uint32_t startX = attr->startX;
  uint32_t startY = attr->startY;
  // uint32_t imgW = attr->imgW;
  uint32_t imgH = attr->imgH;
  int char_num = 0;
  uint32_t pix_x_base = startX;
  uint32_t pix_y_base = startY;

  uint8_t colorY = g_sampleColorMap[attr->color].colorY;
  uint8_t colorU = g_sampleColorMap[attr->color].colorU;
  uint8_t colorV = g_sampleColorMap[attr->color].colorV;

  uint8_t letterSpace = (attr->fontSize / SAMPLE_FONT_SIZE_18 < 2)
                            ? 2
                            : attr->fontSize / SAMPLE_FONT_SIZE_18;
  for (wchar_t ch = *(attr->text + char_num); *(attr->text + char_num) != '\0';
       char_num++) {
    ch = *(attr->text + char_num);
    if (ch == ' ') {
      pix_x_base += attr->fontSize / 2 + 2;
      continue;
    }
    auto bitMap = sampleFreeType::SelectSingleWchar(ch, attr->fontSize);

    int w = bitMap->w;
    int h = bitMap->h;
    pix_y_base = startY - h;

    unsigned char *data = bitMap->buffer;
    for (int j = 0; j < h; j++) {
      for (int i = 0; i < w; i++) {
        uint32_t pix_x = pix_x_base + i;
        uint32_t pix_y = pix_y_base + j;
        if (pix_y < 0 || pix_y >= imgH || pix_x >= attr->imgW || pix_x < 0) {
          continue;
        }
        if (data[j * w + i] != 0) {
          DarwPoint draw_point;
          draw_point.x = pix_x;
          draw_point.y = pix_y;
          yuvSetdata(data_y, data_u, attr->imgFmt, attr->imgW, attr->imgH,
                     draw_point, colorY, colorU, colorV, true);
        }
      }
    }
    pix_x_base += w + letterSpace;
  }
  return 0;
}

int PluginDrawText(DrawTextAttr *attr) {
  if (attr == nullptr || attr->imgData == nullptr || attr->text == nullptr) {
    return lynEPTR;
  }
  wchar_t wText[100];
  auto len = std::mbstowcs(wText, attr->text, 100);
  if (len < 0) {
    return -1;
  }
  DrawTextAttrV2 attrV2;
  attrV2.fontSize = attr->fontSize;
  attrV2.imgData = attr->imgData;
  attrV2.imgFmt = attr->imgFmt;
  attrV2.imgH = attr->imgH;
  attrV2.imgW = attr->imgW;
  attrV2.startX = attr->startX;
  attrV2.startY = attr->startY;
  attrV2.text = wText;
  attrV2.color = SAMPLE_DRAW_COLOR_WHITE;
  return PluginDrawTextV2(&attrV2);
}
